Why normal distributions are normal

Normal by addition

pos <- replicate(1000, sum(runif(16, -1, 1)))
hist(pos)

plot(density(pos))


# Sum of lognormal instead of uniform
pos <- replicate(1e3, sum(rlnorm(1e5)))
hist(pos)

dens(pos, norm.comp = TRUE)

Normal by multiplication

prod(1 + runif(12, 0, 0.1))
[1] 1.874935
growth <- replicate(1e4, prod(1 + runif(12, 0, 0.1)))
dens(growth, norm.comp = TRUE)

big <- replicate(1e4, prod(1 + runif(12, 0, 0.5)))
small <- replicate(1e4, prod(1 + runif(12, 0, 0.01)))
dens(big, norm.comp = TRUE)

dens(small, norm.comp = TRUE)

Normal by log-multiplication

log.big <- replicate(1e4, log(prod(1 + runif(12, 0, 0.5))))
dens(log.big, norm.comp = TRUE)

One consequence of this is that statistical models based on Gaussian distributions connot reliably identify micro-process. This recalls the modeling philosophy from Chapter 1 (page 6). But it also means that these models can do useful work, even when they cannot identify process. If we had to know the development biology of height before we could build a statistical model of height, human biology would be sunk.

A language for describing models

  1. Outcome
  2. Likelihood
  3. Predictors
  4. Relate predictors to outcomes
  5. Priors

Grid approximation

library(rethinking)
library(tidyverse)
data(Howell1)
str(Howell1)
'data.frame':   544 obs. of  4 variables:
 $ height: num  152 140 137 157 145 ...
 $ weight: num  47.8 36.5 31.9 53 41.3 ...
 $ age   : num  63 63 65 41 51 35 32 27 19 54 ...
 $ male  : int  1 0 0 1 0 1 0 1 0 1 ...
head(Howell1$height)
[1] 151.765 139.700 136.525 156.845 145.415 163.830
adults <- filter(Howell1, age >= 18)
nrow(adults)
[1] 352
dens(adults$height)

Define the heights as normally distributed with a mean \(\mu\) and standard deviation \(\sigma\). Provide priors for each parameter.

\[ h_i \sim \mathcal{N}(\mu, \sigma) \\ \mu \sim \mathcal{N}(178, 20) \\ \sigma \sim \mathcal{U}(0, 50) \]

# prior distributions
curve(dnorm(x, 178, 20), from = 100, to = 250)

curve(dunif(x, 0, 50), from = -10, to = 60)


# sampling the prior
sample_mu <- rnorm(1e4, 178, 20)
sample_sigma <- runif(1e4, 0, 50)
prior_h <- rnorm(1e4, sample_mu, sample_sigma)
dens(prior_h)

Using grid approximation to estimate the posterior distribution. Everything is on the log scale to avoid round-to-zero errors. Hence, sums instead of products. Also, rescaling to the maximum before exponentiating (prob = exp(prod - max(prod))) to again avoid round-to-zero errors.

post <- expand_grid(
  mu = seq(140, 160, length.out = 200),
  sigma = seq(4, 9, length.out = 200)
) %>% 
  mutate(LL = map2_dbl(mu, sigma, ~ sum(dnorm(adults$height, 
                                              mean = .x, 
                                              sd = .y, 
                                              log = TRUE))),
         prod = LL + dnorm(mu, 178, 20, TRUE) + dunif(sigma, 0, 50, TRUE),
         prob = exp(prod - max(prod)))
contour_xyz(post$mu, post$sigma, post$prob)

image_xyz(post$mu, post$sigma, post$prob)

sample.rows <- sample(1:nrow(post), 
                      size = 1e4, 
                      replace = TRUE, 
                      prob = post$prob)
sample.mu <- post$mu[sample.rows]
sample.sigma <- post$sigma[sample.rows]

samples <- tibble(
  mu = sample.mu,
  sigma = sample.sigma
)
ggplot(samples, aes(mu, sigma)) +
  geom_point(color = "blue", alpha = 0.15) +
  theme_classic()


dens(samples$mu, adj = 0.9)

dens(samples$sigma)


HPDI(samples$mu)
   |0.89    0.89| 
153.8693 155.1759 
HPDI(samples$sigma)
   |0.89    0.89| 
7.316583 8.221106 

Fitting with map

# function list, in this case the model specification
flist <- alist(
  height ~ dnorm(mu, sigma),
  mu ~ dnorm(178, 20),
  sigma ~ dunif(0, 50)
)
m4.1 <- rethinking::map(flist, data = adults)
precis(m4.1)

Now with a narrow prior

m4.2 <- rethinking::map(
  alist(
    height ~ dnorm(mu, sigma),
    mu ~ dnorm(178, 0.1),
    sigma ~ dunif(0, 50)
  ), 
  data = adults
)
precis(m4.2)

Ruh roh, vcov

# variance-covariance
vcov(m4.1)
                mu        sigma
mu    0.1697389250 0.0002180879
sigma 0.0002180879 0.0849049653
# decompose into vector of variances and correlation matrix
diag(vcov(m4.1))
        mu      sigma 
0.16973892 0.08490497 
cov2cor(vcov(m4.1))
               mu       sigma
mu    1.000000000 0.001816663
sigma 0.001816663 1.000000000

Draw samples from a quadratic approximation

post <- extract.samples(m4.1, n = 1e4)
head(post)
precis(post)
quap posterior: 10000 samples from m4.1
plot(post)

Adding a predictor

plot(height ~ weight, data = adults)

Specifying the model with a predictor

\[ h_i \sim \mathcal{N}(\mu_i, \sigma) \\ \mu_i = \alpha + \beta x_i \\ \alpha \sim \mathcal{N}(178, 100) \\ \beta \sim \mathcal{N}(0, 10) \\ \sigma \sim \mathcal{U}(0, 50) \]

Fit the model

m4.3 <- rethinking::map(
  alist(
    height ~ dnorm(mu, sigma),
    mu <- a + b * weight,
    a ~ dnorm(156, 100),
    b ~ dnorm(0, 10),
    sigma ~ dunif(0, 50)
  ), 
  data = adults
)
precis(m4.3)
cov2cor(vcov(m4.3))
                 a             b         sigma
a      1.000000000 -0.9898830224  0.0006411290
b     -0.989883022  1.0000000000 -0.0006307136
sigma  0.000641129 -0.0006307136  1.0000000000

\(\alpha\) and \(\beta\) are super correlated. Let’s disentangle.

adults2 <- mutate(adults, weight.c = weight - mean(weight))
m4.4 <- rethinking::map(
  alist(
    height ~ dnorm(mu, sigma),
    mu <- a + b * weight.c,
    a ~ dnorm(156, 100),
    b ~ dnorm(0, 10),
    sigma ~ dunif(0, 50)
  ), 
  data = adults2
)
precis(m4.4)
cov2cor(vcov(m4.4))
                  a             b         sigma
a      1.000000e+00 -6.963035e-10  1.809411e-06
b     -6.963035e-10  1.000000e+00 -2.875186e-05
sigma  1.809411e-06 -2.875186e-05  1.000000e+00

Plot posterior with uncertainty

post <- extract.samples(m4.3)
ggplot(adults2, aes(weight, height)) + 
  geom_point() +
  geom_abline(intercept = coef(m4.3)["a"], slope = coef(m4.3)["b"]) +
  geom_abline(aes(intercept = a, slope = b), slice(post, 1:20), alpha = 0.2) +
  theme_classic() +
  theme(aspect.ratio = 1)

Now with a regression interval

hpdi_int <- function(x, post, prob) {
  # use posterior a and b to calculate the distribution around the input
  result <- sapply(x, function(.x) post$a + post$b * .x) %>% 
    # summarize the distribution by the highest posterior density interval
    apply(2, HPDI, prob = prob) %>% 
    # rearrange as a data frame
    t() %>% 
    as.data.frame()
  colnames(result) <- c("low", "high")
  result$x = x
  result[, c("x", "low", "high")]
}
# best estimate line
best_line <- tibble(
  weight = c(30, 65),
  height = coef(m4.3)["a"] + coef(m4.3)["b"] * weight
)
# raw data with best estimate and 99% HPDI
ggplot(adults2, aes(weight, height)) + 
  geom_point(shape = 21, alpha = 0.8) +
  geom_ribbon(aes(x, ymin = low, ymax = high), 
              hpdi_int(seq(30, 65, length.out = 1e2), post, 0.99),
              alpha = 0.5,
              inherit.aes = FALSE) +
  geom_line(data = best_line) +
  theme_classic() +
  theme(aspect.ratio = 1)

The previous plot is the 99% interval of \(\mu\). Incorporate \(\sigma\) to get the prediction interval.

# Simulate heights, not just the mean
sim.height <- sim(m4.3, 
                  data = list(weight = seq(30, 65, length.out = 100)),
                  n = 1e4)
str(sim.height)
 num [1:10000, 1:100] 144 138 135 135 138 ...
height.PI <- apply(sim.height, 2, PI, prob = 0.89) %>% 
  t() %>% 
  as.data.frame()
colnames(height.PI) <- c("low", "high")
height.PI$weight <- seq(30, 65, length.out = 100)

ggplot(adults2, aes(weight, height)) + 
  geom_point(shape = 21, alpha = 0.8) +
  geom_ribbon(aes(x = weight, ymin = low, ymax = high), 
              height.PI,
              inherit.aes = FALSE,
              alpha = 0.2) +
  geom_ribbon(aes(x, ymin = low, ymax = high), 
              hpdi_int(seq(30, 65, length.out = 1e2), post, 0.89),
              alpha = 0.6,
              inherit.aes = FALSE) +
  geom_line(data = best_line) +
  theme_classic() +
  theme(aspect.ratio = 1)

This figure has the raw data, the 89% plausible \(\mu\), and the 89% predicted data.

Polynomial regression

Fit the polynomial model:

\[ h_i \sim \mathcal{N}(\mu_i, \sigma) \\ \mu_i = \alpha + \beta_1 x_i + \beta_2 x_i^2\\ \alpha \sim \mathcal{N}(178, 100) \\ \beta_1 \sim \mathcal{N}(0, 10) \\ \beta_2 \sim \mathcal{N}(0, 10) \\ \sigma \sim \mathcal{U}(0, 50) \]

d <- Howell1 %>% 
  mutate(std_weight = (weight - mean(weight)) / sd(weight),
         std_weight2 = std_weight^2)

m4.5 <- rethinking::map(
  alist(
    height ~ dnorm(mu, sigma),
    mu <- a + b1 * std_weight + b2 * std_weight2,
    a ~ dnorm(178, 100),
    b1 ~ dnorm(0, 10),
    b2 ~ dnorm(0, 10),
    sigma ~ dunif(0, 50)
  ), 
  data = d
)
precis(m4.5)

Plot the results

# summarize model
seq_weight <- seq(-2.2, 2, length.out = 30)
pred_dat <- list(std_weight = seq_weight, std_weight2 = seq_weight^2)
mu <- link(m4.5, data = pred_dat)
mu.mean <- apply(mu, 2, mean)
mu.PI <- apply(mu, 2, PI, prob = 0.89)
sim.height <- sim(m4.5, data = pred_dat)
height.PI <- apply(sim.height, 2, PI, prob = 0.89)

# plot fit
pred_tbl <- tibble(
  std_weight = seq_weight, 
  mu_mean = mu.mean,
  mu_low = mu.PI[1, ],
  mu_high = mu.PI[2, ],
  height_low = height.PI[1, ],
  height_high = height.PI[2, ]
) %>% 
  mutate(weight = std_weight * sd(d$weight) + mean(d$weight))

ggplot(d, aes(weight, height)) + 
  geom_point(shape = 21, alpha = 0.5) +
  geom_line(aes(y = mu_mean), pred_tbl) +
  geom_ribbon(aes(weight, ymin = mu_low, ymax = mu_high),
              pred_tbl,
              inherit.aes = FALSE,
              alpha = 0.6) +
  geom_ribbon(aes(weight, ymin = height_low, ymax = height_high),
              pred_tbl,
              inherit.aes = FALSE,
              alpha = 0.3) +
  theme_classic() +
  theme(aspect.ratio = 1)

Exercises

Easy

4e1 line 1 is the likelihood

4e2 2 parameters

4e3

\[ p(\mu, \sigma | y) = \frac{\prod_i \mathcal{N}(h_i | \mu, \sigma) \mathcal{N}(\mu | 0, 10) \mathcal{U}(\sigma | 0, 10)}{\int \int \prod_i \mathcal{N}(h_i | \mu, \sigma) \mathcal{N}(\mu | 0, 10) \mathcal{U}(\sigma | 0, 10) d\mu d\sigma} \]

4e4 line 2 is the linear model

4e5 3 parameters

Medium

4m1

sim_heights <- rnorm(1e4, rnorm(1e4, 0, 10), runif(1e4, 0, 10))
plot(density(sim_heights))
curve(dnorm(x, mean = mean(sim_heights), sd = sd(sim_heights)), 
      col = "blue", lty = 3, add = TRUE)

4m2

m4m2_form <- alist(
  y ~ dnorm(mu, sigma),
  mu ~ dnorm(0, 10),
  sigma ~ dunif(0, 10)
)

4m3

\[ y_i \sim \mathcal{N}(\mu_i, \sigma) \\ \mu_i = \alpha + \beta x_i \\ \alpha \sim \mathcal{N}(0, 50) \\ \beta \sim \mathcal{U}(0, 10) \\ \sigma \sim \mathcal{U}(0, 50) \]

4m4

m4m2_form <- alist(
  height ~ dnorm(mu, sigma),
  mu = a + b * year,
  a ~ dnorm(100, 10),
  b ~ dnorm(2, 1),
  sigma ~ dunif(0, 20)
)

4m5

Set mean of a to 120. For b, use log(b) ~ dnorm(...), which forces growth rate to be positive.

4m6

sigma ~ dunif(0, sqrt(64)). i.e. variance must be between 0 and 64.

Hard

4h1

weight <- c(46.95, 43.72, 64.78, 32.59, 54.63)
std_weight <- (weight - mean(d$weight)) / sd(d$weight)
sim_weight <- sim(m4.5, data = list(std_weight = std_weight, std_weight2 = std_weight^2))

mean_height <- apply(sim_weight, 2, mean)
pi_height <- apply(sim_weight, 2, PI)
pi_height_fmt <- sprintf("%0.1f - %0.1f", pi_height[1, ], pi_height[2, ])
tibble(
  Individual = 1:5, 
  weight = weight, 
  `expected height` = mean_height,
  `89% interval` = pi_height_fmt
)
kids <- filter(Howell1, age < 18) %>% 
  mutate(std_weight = (weight - mean(weight)) / sd(weight))
m4h2 <- rethinking::map(
  alist(
    height ~ dnorm(mu, sigma),
    mu <- a + b * std_weight,
    a ~ dnorm(mean(height), 10),
    b ~ dnorm(0, 10),
    sigma ~ dunif(0, 25)
  ),
  data = kids
)

post <- extract.samples(m4h2, n = 1e4)
dens(post$a)

dens(post$b)

dens(post$sigma)


pred_tbl <- tibble(
  std_weight = seq(min(kids$std_weight), 
                   max(kids$std_weight), 
                   length.out = 100),
  weight = std_weight * sd(kids$weight) + mean(kids$weight),
  mu = coef(m4h2)["a"] + coef(m4h2)["b"] * std_weight
)

ggplot(kids, aes(weight, height)) +
  geom_point(shape = 21) +
  geom_line(aes(y = mu),
            pred_tbl) +
  theme_classic() +
  theme(aspect.ratio = 1)


mean(post$b) / sd(kids$weight)
[1] 2.709049
PI(post$b) / sd(kids$weight)
      5%      94% 
2.601078 2.815892 

On average, an increase of 10 units in weight correlates with an increase of 27.1 (26.0 - 28.2) units o height.

# summarize model
seq_weight <- seq(min(kids$std_weight), 
                  max(kids$std_weight), 
                  length.out = 50)
pred_dat <- list(std_weight = seq_weight)
mu <- link(m4h2, data = pred_dat)
mu.mean <- apply(mu, 2, mean)
mu.PI <- apply(mu, 2, PI, prob = 0.89)
sim.height <- sim(m4h2, data = pred_dat)
height.PI <- apply(sim.height, 2, PI, prob = 0.89)

# plot fit
pred_tbl <- tibble(
  std_weight = seq_weight, 
  mu_mean = mu.mean,
  mu_low = mu.PI[1, ],
  mu_high = mu.PI[2, ],
  height_low = height.PI[1, ],
  height_high = height.PI[2, ]
) %>% 
  mutate(weight = std_weight * sd(kids$weight) + mean(kids$weight))

ggplot(kids, aes(weight, height)) + 
  geom_point(shape = 21, alpha = 0.5) +
  geom_line(aes(y = mu_mean), pred_tbl) +
  geom_ribbon(aes(weight, ymin = mu_low, ymax = mu_high),
              pred_tbl,
              inherit.aes = FALSE,
              alpha = 0.6) +
  geom_ribbon(aes(weight, ymin = height_low, ymax = height_high),
              pred_tbl,
              inherit.aes = FALSE,
              alpha = 0.3) +
  theme_classic() +
  theme(aspect.ratio = 1)

The model over-predicts height for low/high weights and under-predicts height for weights near the mean. A polynomial or asymptotic model would capture the curvature better.

4h3

m4h3 <- rethinking::map(
  alist(
    height ~ dnorm(mu, sigma),
    mu <- a + b * log(weight),
    a ~ dnorm(178, 100),
    b ~ dnorm(0, 100),
    sigma ~ dunif(0, 50)
  ),
  data = Howell1
)

# summarize model
seq_weight <- seq(min(Howell1$weight), 
                  max(Howell1$weight), 
                  length.out = 50)
pred_dat <- list(weight = seq_weight)
mu <- link(m4h3, data = pred_dat)
mu.mean <- apply(mu, 2, mean)
mu.PI <- apply(mu, 2, PI, prob = 0.97)
sim.height <- sim(m4h3, data = pred_dat)
height.PI <- apply(sim.height, 2, PI, prob = 0.97)

# plot fit
pred_tbl <- tibble(
  weight = seq_weight, 
  mu_mean = mu.mean,
  mu_low = mu.PI[1, ],
  mu_high = mu.PI[2, ],
  height_low = height.PI[1, ],
  height_high = height.PI[2, ]
)

ggplot(Howell1, aes(weight, height)) + 
  geom_point(shape = 21, alpha = 0.5) +
  geom_line(aes(y = mu_mean), pred_tbl) +
  geom_ribbon(aes(weight, ymin = mu_low, ymax = mu_high),
              pred_tbl,
              inherit.aes = FALSE,
              alpha = 0.6) +
  geom_ribbon(aes(weight, ymin = height_low, ymax = height_high),
              pred_tbl,
              inherit.aes = FALSE,
              alpha = 0.3) +
  theme_classic() +
  theme(aspect.ratio = 1)

LS0tCnRpdGxlOiAnQ2ggNDogTGluZWFyIE1vZGVscycKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMjIFdoeSBub3JtYWwgZGlzdHJpYnV0aW9ucyBhcmUgbm9ybWFsCgojIyMgTm9ybWFsIGJ5IGFkZGl0aW9uCgpgYGB7ciA0LjF9CnBvcyA8LSByZXBsaWNhdGUoMTAwMCwgc3VtKHJ1bmlmKDE2LCAtMSwgMSkpKQpoaXN0KHBvcykKcGxvdChkZW5zaXR5KHBvcykpCgojIFN1bSBvZiBsb2dub3JtYWwgaW5zdGVhZCBvZiB1bmlmb3JtCnBvcyA8LSByZXBsaWNhdGUoMWUzLCBzdW0ocmxub3JtKDFlNSkpKQpoaXN0KHBvcykKZGVucyhwb3MsIG5vcm0uY29tcCA9IFRSVUUpCmBgYAoKIyMjIE5vcm1hbCBieSBtdWx0aXBsaWNhdGlvbgoKYGBge3IgNC4yfQpwcm9kKDEgKyBydW5pZigxMiwgMCwgMC4xKSkKZ3Jvd3RoIDwtIHJlcGxpY2F0ZSgxZTQsIHByb2QoMSArIHJ1bmlmKDEyLCAwLCAwLjEpKSkKZGVucyhncm93dGgsIG5vcm0uY29tcCA9IFRSVUUpCmBgYAoKYGBge3IgNC40fQpiaWcgPC0gcmVwbGljYXRlKDFlNCwgcHJvZCgxICsgcnVuaWYoMTIsIDAsIDAuNSkpKQpzbWFsbCA8LSByZXBsaWNhdGUoMWU0LCBwcm9kKDEgKyBydW5pZigxMiwgMCwgMC4wMSkpKQpkZW5zKGJpZywgbm9ybS5jb21wID0gVFJVRSkKZGVucyhzbWFsbCwgbm9ybS5jb21wID0gVFJVRSkKYGBgCgojIyMgTm9ybWFsIGJ5IGxvZy1tdWx0aXBsaWNhdGlvbgoKYGBge3IgNC41fQpsb2cuYmlnIDwtIHJlcGxpY2F0ZSgxZTQsIGxvZyhwcm9kKDEgKyBydW5pZigxMiwgMCwgMC41KSkpKQpkZW5zKGxvZy5iaWcsIG5vcm0uY29tcCA9IFRSVUUpCmBgYAoKPiBPbmUgY29uc2VxdWVuY2Ugb2YgdGhpcyBpcyB0aGF0IHN0YXRpc3RpY2FsIG1vZGVscyBiYXNlZCBvbiBHYXVzc2lhbiBkaXN0cmlidXRpb25zIGNvbm5vdCByZWxpYWJseSBpZGVudGlmeSBtaWNyby1wcm9jZXNzLiBUaGlzIHJlY2FsbHMgdGhlIG1vZGVsaW5nIHBoaWxvc29waHkgZnJvbSBDaGFwdGVyIDEgKHBhZ2UgNikuIEJ1dCBpdCBhbHNvIG1lYW5zIHRoYXQgdGhlc2UgbW9kZWxzIGNhbiBkbyB1c2VmdWwgd29yaywgZXZlbiB3aGVuIHRoZXkgY2Fubm90IGlkZW50aWZ5IHByb2Nlc3MuIElmIHdlIGhhZCB0byBrbm93IHRoZSBkZXZlbG9wbWVudCBiaW9sb2d5IG9mIGhlaWdodCBiZWZvcmUgd2UgY291bGQgYnVpbGQgYSBzdGF0aXN0aWNhbCBtb2RlbCBvZiBoZWlnaHQsIGh1bWFuIGJpb2xvZ3kgd291bGQgYmUgc3Vuay4KCiMjIEEgbGFuZ3VhZ2UgZm9yIGRlc2NyaWJpbmcgbW9kZWxzCgoxLiBPdXRjb21lCjIuIExpa2VsaWhvb2QKMy4gUHJlZGljdG9ycwo0LiBSZWxhdGUgcHJlZGljdG9ycyB0byBvdXRjb21lcwo1LiBQcmlvcnMKCiMjIyBHcmlkIGFwcHJveGltYXRpb24KCmBgYHtyIDQuN30KbGlicmFyeShyZXRoaW5raW5nKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZGF0YShIb3dlbGwxKQpzdHIoSG93ZWxsMSkKaGVhZChIb3dlbGwxJGhlaWdodCkKYWR1bHRzIDwtIGZpbHRlcihIb3dlbGwxLCBhZ2UgPj0gMTgpCm5yb3coYWR1bHRzKQpkZW5zKGFkdWx0cyRoZWlnaHQpCmBgYAoKRGVmaW5lIHRoZSBoZWlnaHRzIGFzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggYSBtZWFuICRcbXUkIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gJFxzaWdtYSQuIFByb3ZpZGUgcHJpb3JzIGZvciBlYWNoIHBhcmFtZXRlci4KCiQkCmhfaSBcc2ltIFxtYXRoY2Fse059KFxtdSwgXHNpZ21hKSBcXApcbXUgXHNpbSBcbWF0aGNhbHtOfSgxNzgsIDIwKSBcXApcc2lnbWEgXHNpbSBcbWF0aGNhbHtVfSgwLCA1MCkKJCQKCmBgYHtyIDQuMTF9CiMgcHJpb3IgZGlzdHJpYnV0aW9ucwpjdXJ2ZShkbm9ybSh4LCAxNzgsIDIwKSwgZnJvbSA9IDEwMCwgdG8gPSAyNTApCmN1cnZlKGR1bmlmKHgsIDAsIDUwKSwgZnJvbSA9IC0xMCwgdG8gPSA2MCkKCiMgc2FtcGxpbmcgdGhlIHByaW9yCnNhbXBsZV9tdSA8LSBybm9ybSgxZTQsIDE3OCwgMjApCnNhbXBsZV9zaWdtYSA8LSBydW5pZigxZTQsIDAsIDUwKQpwcmlvcl9oIDwtIHJub3JtKDFlNCwgc2FtcGxlX211LCBzYW1wbGVfc2lnbWEpCmRlbnMocHJpb3JfaCkKYGBgCgpVc2luZyBncmlkIGFwcHJveGltYXRpb24gdG8gZXN0aW1hdGUgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24uIEV2ZXJ5dGhpbmcgaXMgb24gdGhlIGxvZyBzY2FsZSB0byBhdm9pZCByb3VuZC10by16ZXJvIGVycm9ycy4gSGVuY2UsIHN1bXMgaW5zdGVhZCBvZiBwcm9kdWN0cy4gQWxzbywgcmVzY2FsaW5nIHRvIHRoZSBtYXhpbXVtIGJlZm9yZSBleHBvbmVudGlhdGluZyAoYHByb2IgPSBleHAocHJvZCAtIG1heChwcm9kKSlgKSB0byBhZ2FpbiBhdm9pZCByb3VuZC10by16ZXJvIGVycm9ycy4KCmBgYHtyIDQuMTR9CnBvc3QgPC0gZXhwYW5kX2dyaWQoCiAgbXUgPSBzZXEoMTQwLCAxNjAsIGxlbmd0aC5vdXQgPSAyMDApLAogIHNpZ21hID0gc2VxKDQsIDksIGxlbmd0aC5vdXQgPSAyMDApCikgJT4lIAogIG11dGF0ZShMTCA9IG1hcDJfZGJsKG11LCBzaWdtYSwgfiBzdW0oZG5vcm0oYWR1bHRzJGhlaWdodCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuID0gLngsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2QgPSAueSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cgPSBUUlVFKSkpLAogICAgICAgICBwcm9kID0gTEwgKyBkbm9ybShtdSwgMTc4LCAyMCwgVFJVRSkgKyBkdW5pZihzaWdtYSwgMCwgNTAsIFRSVUUpLAogICAgICAgICBwcm9iID0gZXhwKHByb2QgLSBtYXgocHJvZCkpKQpjb250b3VyX3h5eihwb3N0JG11LCBwb3N0JHNpZ21hLCBwb3N0JHByb2IpCmltYWdlX3h5eihwb3N0JG11LCBwb3N0JHNpZ21hLCBwb3N0JHByb2IpCmBgYAoKYGBge3IgNC4xN30Kc2FtcGxlLnJvd3MgPC0gc2FtcGxlKDE6bnJvdyhwb3N0KSwgCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMWU0LCAKICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgIHByb2IgPSBwb3N0JHByb2IpCnNhbXBsZS5tdSA8LSBwb3N0JG11W3NhbXBsZS5yb3dzXQpzYW1wbGUuc2lnbWEgPC0gcG9zdCRzaWdtYVtzYW1wbGUucm93c10KCnNhbXBsZXMgPC0gdGliYmxlKAogIG11ID0gc2FtcGxlLm11LAogIHNpZ21hID0gc2FtcGxlLnNpZ21hCikKZ2dwbG90KHNhbXBsZXMsIGFlcyhtdSwgc2lnbWEpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjE1KSArCiAgdGhlbWVfY2xhc3NpYygpCgpkZW5zKHNhbXBsZXMkbXUsIGFkaiA9IDAuOSkKZGVucyhzYW1wbGVzJHNpZ21hKQoKSFBESShzYW1wbGVzJG11KQpIUERJKHNhbXBsZXMkc2lnbWEpCmBgYAoKIyMjIEZpdHRpbmcgd2l0aCBgbWFwYAoKYGBge3IgNC4yNX0KIyBmdW5jdGlvbiBsaXN0LCBpbiB0aGlzIGNhc2UgdGhlIG1vZGVsIHNwZWNpZmljYXRpb24KZmxpc3QgPC0gYWxpc3QoCiAgaGVpZ2h0IH4gZG5vcm0obXUsIHNpZ21hKSwKICBtdSB+IGRub3JtKDE3OCwgMjApLAogIHNpZ21hIH4gZHVuaWYoMCwgNTApCikKbTQuMSA8LSByZXRoaW5raW5nOjptYXAoZmxpc3QsIGRhdGEgPSBhZHVsdHMpCnByZWNpcyhtNC4xKQpgYGAKCk5vdyB3aXRoIGEgbmFycm93IHByaW9yCmBgYHtyIDQuMjl9Cm00LjIgPC0gcmV0aGlua2luZzo6bWFwKAogIGFsaXN0KAogICAgaGVpZ2h0IH4gZG5vcm0obXUsIHNpZ21hKSwKICAgIG11IH4gZG5vcm0oMTc4LCAwLjEpLAogICAgc2lnbWEgfiBkdW5pZigwLCA1MCkKICApLCAKICBkYXRhID0gYWR1bHRzCikKcHJlY2lzKG00LjIpCmBgYAoKIyMjIFJ1aCByb2gsIHZjb3YKCmBgYHtyIDQuMzB9CiMgdmFyaWFuY2UtY292YXJpYW5jZQp2Y292KG00LjEpCiMgZGVjb21wb3NlIGludG8gdmVjdG9yIG9mIHZhcmlhbmNlcyBhbmQgY29ycmVsYXRpb24gbWF0cml4CmRpYWcodmNvdihtNC4xKSkKY292MmNvcih2Y292KG00LjEpKQpgYGAKCkRyYXcgc2FtcGxlcyBmcm9tIGEgcXVhZHJhdGljIGFwcHJveGltYXRpb24KCmBgYHtyIDQuMzJ9CnBvc3QgPC0gZXh0cmFjdC5zYW1wbGVzKG00LjEsIG4gPSAxZTQpCmhlYWQocG9zdCkKcHJlY2lzKHBvc3QpCnBsb3QocG9zdCkKYGBgCgojIyBBZGRpbmcgYSBwcmVkaWN0b3IKCmBgYHtyIDQuMzd9CnBsb3QoaGVpZ2h0IH4gd2VpZ2h0LCBkYXRhID0gYWR1bHRzKQpgYGAKClNwZWNpZnlpbmcgdGhlIG1vZGVsIHdpdGggYSBwcmVkaWN0b3IKCiQkCmhfaSBcc2ltIFxtYXRoY2Fse059KFxtdV9pLCBcc2lnbWEpIFxcClxtdV9pID0gXGFscGhhICsgXGJldGEgeF9pIFxcClxhbHBoYSBcc2ltIFxtYXRoY2Fse059KDE3OCwgMTAwKSBcXApcYmV0YSBcc2ltIFxtYXRoY2Fse059KDAsIDEwKSBcXApcc2lnbWEgXHNpbSBcbWF0aGNhbHtVfSgwLCA1MCkKJCQKCkZpdCB0aGUgbW9kZWwKCmBgYHtyIDQuMzh9Cm00LjMgPC0gcmV0aGlua2luZzo6bWFwKAogIGFsaXN0KAogICAgaGVpZ2h0IH4gZG5vcm0obXUsIHNpZ21hKSwKICAgIG11IDwtIGEgKyBiICogd2VpZ2h0LAogICAgYSB+IGRub3JtKDE1NiwgMTAwKSwKICAgIGIgfiBkbm9ybSgwLCAxMCksCiAgICBzaWdtYSB+IGR1bmlmKDAsIDUwKQogICksIAogIGRhdGEgPSBhZHVsdHMKKQpwcmVjaXMobTQuMykKY292MmNvcih2Y292KG00LjMpKQpgYGAKCiRcYWxwaGEkIGFuZCAkXGJldGEkIGFyZSBzdXBlciBjb3JyZWxhdGVkLiBMZXQncyBkaXNlbnRhbmdsZS4KCmBgYHtyIDQuNDJ9CmFkdWx0czIgPC0gbXV0YXRlKGFkdWx0cywgd2VpZ2h0LmMgPSB3ZWlnaHQgLSBtZWFuKHdlaWdodCkpCm00LjQgPC0gcmV0aGlua2luZzo6bWFwKAogIGFsaXN0KAogICAgaGVpZ2h0IH4gZG5vcm0obXUsIHNpZ21hKSwKICAgIG11IDwtIGEgKyBiICogd2VpZ2h0LmMsCiAgICBhIH4gZG5vcm0oMTU2LCAxMDApLAogICAgYiB+IGRub3JtKDAsIDEwKSwKICAgIHNpZ21hIH4gZHVuaWYoMCwgNTApCiAgKSwgCiAgZGF0YSA9IGFkdWx0czIKKQpwcmVjaXMobTQuNCkKY292MmNvcih2Y292KG00LjQpKQpgYGAKClBsb3QgcG9zdGVyaW9yIHdpdGggdW5jZXJ0YWludHkKCmBgYHtyIDQuNDV9CnBvc3QgPC0gZXh0cmFjdC5zYW1wbGVzKG00LjMpCmdncGxvdChhZHVsdHMyLCBhZXMod2VpZ2h0LCBoZWlnaHQpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gY29lZihtNC4zKVsiYSJdLCBzbG9wZSA9IGNvZWYobTQuMylbImIiXSkgKwogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBhLCBzbG9wZSA9IGIpLCBzbGljZShwb3N0LCAxOjIwKSwgYWxwaGEgPSAwLjIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKTm93IHdpdGggYSByZWdyZXNzaW9uIGludGVydmFsCgpgYGB7ciA0LjUzfQpocGRpX2ludCA8LSBmdW5jdGlvbih4LCBwb3N0LCBwcm9iKSB7CiAgIyB1c2UgcG9zdGVyaW9yIGEgYW5kIGIgdG8gY2FsY3VsYXRlIHRoZSBkaXN0cmlidXRpb24gYXJvdW5kIHRoZSBpbnB1dAogIHJlc3VsdCA8LSBzYXBwbHkoeCwgZnVuY3Rpb24oLngpIHBvc3QkYSArIHBvc3QkYiAqIC54KSAlPiUgCiAgICAjIHN1bW1hcml6ZSB0aGUgZGlzdHJpYnV0aW9uIGJ5IHRoZSBoaWdoZXN0IHBvc3RlcmlvciBkZW5zaXR5IGludGVydmFsCiAgICBhcHBseSgyLCBIUERJLCBwcm9iID0gcHJvYikgJT4lIAogICAgIyByZWFycmFuZ2UgYXMgYSBkYXRhIGZyYW1lCiAgICB0KCkgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpCiAgY29sbmFtZXMocmVzdWx0KSA8LSBjKCJsb3ciLCAiaGlnaCIpCiAgcmVzdWx0JHggPSB4CiAgcmVzdWx0WywgYygieCIsICJsb3ciLCAiaGlnaCIpXQp9CiMgYmVzdCBlc3RpbWF0ZSBsaW5lCmJlc3RfbGluZSA8LSB0aWJibGUoCiAgd2VpZ2h0ID0gYygzMCwgNjUpLAogIGhlaWdodCA9IGNvZWYobTQuMylbImEiXSArIGNvZWYobTQuMylbImIiXSAqIHdlaWdodAopCiMgcmF3IGRhdGEgd2l0aCBiZXN0IGVzdGltYXRlIGFuZCA5OSUgSFBESQpnZ3Bsb3QoYWR1bHRzMiwgYWVzKHdlaWdodCwgaGVpZ2h0KSkgKyAKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGFscGhhID0gMC44KSArCiAgZ2VvbV9yaWJib24oYWVzKHgsIHltaW4gPSBsb3csIHltYXggPSBoaWdoKSwgCiAgICAgICAgICAgICAgaHBkaV9pbnQoc2VxKDMwLCA2NSwgbGVuZ3RoLm91dCA9IDFlMiksIHBvc3QsIDAuOTkpLAogICAgICAgICAgICAgIGFscGhhID0gMC41LAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICBnZW9tX2xpbmUoZGF0YSA9IGJlc3RfbGluZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKYGBgCgpUaGUgcHJldmlvdXMgcGxvdCBpcyB0aGUgOTklIGludGVydmFsIG9mICoqJFxtdSQqKi4gSW5jb3Jwb3JhdGUgJFxzaWdtYSQgdG8gZ2V0IHRoZSBwcmVkaWN0aW9uIGludGVydmFsLgoKYGBge3IgNC41OX0KIyBTaW11bGF0ZSBoZWlnaHRzLCBub3QganVzdCB0aGUgbWVhbgpzaW0uaGVpZ2h0IDwtIHNpbShtNC4zLCAKICAgICAgICAgICAgICAgICAgZGF0YSA9IGxpc3Qod2VpZ2h0ID0gc2VxKDMwLCA2NSwgbGVuZ3RoLm91dCA9IDEwMCkpLAogICAgICAgICAgICAgICAgICBuID0gMWU0KQpzdHIoc2ltLmhlaWdodCkKaGVpZ2h0LlBJIDwtIGFwcGx5KHNpbS5oZWlnaHQsIDIsIFBJLCBwcm9iID0gMC44OSkgJT4lIAogIHQoKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCmNvbG5hbWVzKGhlaWdodC5QSSkgPC0gYygibG93IiwgImhpZ2giKQpoZWlnaHQuUEkkd2VpZ2h0IDwtIHNlcSgzMCwgNjUsIGxlbmd0aC5vdXQgPSAxMDApCgpnZ3Bsb3QoYWR1bHRzMiwgYWVzKHdlaWdodCwgaGVpZ2h0KSkgKyAKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGFscGhhID0gMC44KSArCiAgZ2VvbV9yaWJib24oYWVzKHggPSB3ZWlnaHQsIHltaW4gPSBsb3csIHltYXggPSBoaWdoKSwgCiAgICAgICAgICAgICAgaGVpZ2h0LlBJLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjIpICsKICBnZW9tX3JpYmJvbihhZXMoeCwgeW1pbiA9IGxvdywgeW1heCA9IGhpZ2gpLCAKICAgICAgICAgICAgICBocGRpX2ludChzZXEoMzAsIDY1LCBsZW5ndGgub3V0ID0gMWUyKSwgcG9zdCwgMC44OSksCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjYsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fbGluZShkYXRhID0gYmVzdF9saW5lKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAKClRoaXMgZmlndXJlIGhhcyB0aGUgcmF3IGRhdGEsIHRoZSA4OSUgcGxhdXNpYmxlICRcbXUkLCBhbmQgdGhlIDg5JSBwcmVkaWN0ZWQgZGF0YS4KCiMjIFBvbHlub21pYWwgcmVncmVzc2lvbiAKCkZpdCB0aGUgcG9seW5vbWlhbCBtb2RlbDoKCiQkCmhfaSBcc2ltIFxtYXRoY2Fse059KFxtdV9pLCBcc2lnbWEpIFxcClxtdV9pID0gXGFscGhhICsgXGJldGFfMSB4X2kgKyBcYmV0YV8yIHhfaV4yXFwKXGFscGhhIFxzaW0gXG1hdGhjYWx7Tn0oMTc4LCAxMDApIFxcClxiZXRhXzEgXHNpbSBcbWF0aGNhbHtOfSgwLCAxMCkgXFwKXGJldGFfMiBcc2ltIFxtYXRoY2Fse059KDAsIDEwKSBcXApcc2lnbWEgXHNpbSBcbWF0aGNhbHtVfSgwLCA1MCkKJCQKCmBgYHtyIDQuNjZ9CmQgPC0gSG93ZWxsMSAlPiUgCiAgbXV0YXRlKHN0ZF93ZWlnaHQgPSAod2VpZ2h0IC0gbWVhbih3ZWlnaHQpKSAvIHNkKHdlaWdodCksCiAgICAgICAgIHN0ZF93ZWlnaHQyID0gc3RkX3dlaWdodF4yKQoKbTQuNSA8LSByZXRoaW5raW5nOjptYXAoCiAgYWxpc3QoCiAgICBoZWlnaHQgfiBkbm9ybShtdSwgc2lnbWEpLAogICAgbXUgPC0gYSArIGIxICogc3RkX3dlaWdodCArIGIyICogc3RkX3dlaWdodDIsCiAgICBhIH4gZG5vcm0oMTc4LCAxMDApLAogICAgYjEgfiBkbm9ybSgwLCAxMCksCiAgICBiMiB+IGRub3JtKDAsIDEwKSwKICAgIHNpZ21hIH4gZHVuaWYoMCwgNTApCiAgKSwgCiAgZGF0YSA9IGQKKQpwcmVjaXMobTQuNSkKYGBgCgpQbG90IHRoZSByZXN1bHRzCgpgYGB7ciA0LjY4fQojIHN1bW1hcml6ZSBtb2RlbApzZXFfd2VpZ2h0IDwtIHNlcSgtMi4yLCAyLCBsZW5ndGgub3V0ID0gMzApCnByZWRfZGF0IDwtIGxpc3Qoc3RkX3dlaWdodCA9IHNlcV93ZWlnaHQsIHN0ZF93ZWlnaHQyID0gc2VxX3dlaWdodF4yKQptdSA8LSBsaW5rKG00LjUsIGRhdGEgPSBwcmVkX2RhdCkKbXUubWVhbiA8LSBhcHBseShtdSwgMiwgbWVhbikKbXUuUEkgPC0gYXBwbHkobXUsIDIsIFBJLCBwcm9iID0gMC44OSkKc2ltLmhlaWdodCA8LSBzaW0obTQuNSwgZGF0YSA9IHByZWRfZGF0KQpoZWlnaHQuUEkgPC0gYXBwbHkoc2ltLmhlaWdodCwgMiwgUEksIHByb2IgPSAwLjg5KQoKIyBwbG90IGZpdApwcmVkX3RibCA8LSB0aWJibGUoCiAgc3RkX3dlaWdodCA9IHNlcV93ZWlnaHQsIAogIG11X21lYW4gPSBtdS5tZWFuLAogIG11X2xvdyA9IG11LlBJWzEsIF0sCiAgbXVfaGlnaCA9IG11LlBJWzIsIF0sCiAgaGVpZ2h0X2xvdyA9IGhlaWdodC5QSVsxLCBdLAogIGhlaWdodF9oaWdoID0gaGVpZ2h0LlBJWzIsIF0KKSAlPiUgCiAgbXV0YXRlKHdlaWdodCA9IHN0ZF93ZWlnaHQgKiBzZChkJHdlaWdodCkgKyBtZWFuKGQkd2VpZ2h0KSkKCmdncGxvdChkLCBhZXMod2VpZ2h0LCBoZWlnaHQpKSArIAogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBtdV9tZWFuKSwgcHJlZF90YmwpICsKICBnZW9tX3JpYmJvbihhZXMod2VpZ2h0LCB5bWluID0gbXVfbG93LCB5bWF4ID0gbXVfaGlnaCksCiAgICAgICAgICAgICAgcHJlZF90YmwsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICBhbHBoYSA9IDAuNikgKwogIGdlb21fcmliYm9uKGFlcyh3ZWlnaHQsIHltaW4gPSBoZWlnaHRfbG93LCB5bWF4ID0gaGVpZ2h0X2hpZ2gpLAogICAgICAgICAgICAgIHByZWRfdGJsLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKIyMgRXhlcmNpc2VzCgojIyMgRWFzeQoKNGUxIGxpbmUgMSBpcyB0aGUgbGlrZWxpaG9vZAoKNGUyIDIgcGFyYW1ldGVycwoKNGUzCgokJApwKFxtdSwgXHNpZ21hIHwgeSkgPSBcZnJhY3tccHJvZF9pIFxtYXRoY2Fse059KGhfaSB8IFxtdSwgXHNpZ21hKSBcbWF0aGNhbHtOfShcbXUgfCAwLCAxMCkgXG1hdGhjYWx7VX0oXHNpZ21hIHwgMCwgMTApfXtcaW50IFxpbnQgXHByb2RfaSBcbWF0aGNhbHtOfShoX2kgfCBcbXUsIFxzaWdtYSkgXG1hdGhjYWx7Tn0oXG11IHwgMCwgMTApIFxtYXRoY2Fse1V9KFxzaWdtYSB8IDAsIDEwKSBkXG11IGRcc2lnbWF9CiQkCgo0ZTQgbGluZSAyIGlzIHRoZSBsaW5lYXIgbW9kZWwKCjRlNSAzIHBhcmFtZXRlcnMKCiMjIyBNZWRpdW0KCjRtMQoKYGBge3IgNG0xfQpzaW1faGVpZ2h0cyA8LSBybm9ybSgxZTQsIHJub3JtKDFlNCwgMCwgMTApLCBydW5pZigxZTQsIDAsIDEwKSkKcGxvdChkZW5zaXR5KHNpbV9oZWlnaHRzKSkKY3VydmUoZG5vcm0oeCwgbWVhbiA9IG1lYW4oc2ltX2hlaWdodHMpLCBzZCA9IHNkKHNpbV9oZWlnaHRzKSksIAogICAgICBjb2wgPSAiYmx1ZSIsIGx0eSA9IDMsIGFkZCA9IFRSVUUpCmBgYAoKNG0yCgpgYGB7ciA0bTJ9Cm00bTJfZm9ybSA8LSBhbGlzdCgKICB5IH4gZG5vcm0obXUsIHNpZ21hKSwKICBtdSB+IGRub3JtKDAsIDEwKSwKICBzaWdtYSB+IGR1bmlmKDAsIDEwKQopCmBgYAoKNG0zCgokJAp5X2kgXHNpbSBcbWF0aGNhbHtOfShcbXVfaSwgXHNpZ21hKSBcXApcbXVfaSA9IFxhbHBoYSArIFxiZXRhIHhfaSBcXApcYWxwaGEgXHNpbSBcbWF0aGNhbHtOfSgwLCA1MCkgXFwKXGJldGEgXHNpbSBcbWF0aGNhbHtVfSgwLCAxMCkgXFwKXHNpZ21hIFxzaW0gXG1hdGhjYWx7VX0oMCwgNTApCiQkCgo0bTQKCmBgYHtyIDRtNH0KbTRtMl9mb3JtIDwtIGFsaXN0KAogIGhlaWdodCB+IGRub3JtKG11LCBzaWdtYSksCiAgbXUgPSBhICsgYiAqIHllYXIsCiAgYSB+IGRub3JtKDEwMCwgMTApLAogIGIgfiBkbm9ybSgyLCAxKSwKICBzaWdtYSB+IGR1bmlmKDAsIDIwKQopCmBgYAoKNG01CgpTZXQgbWVhbiBvZiBgYWAgdG8gMTIwLiBGb3IgYGJgLCB1c2UgYGxvZyhiKSB+IGRub3JtKC4uLilgLCB3aGljaCBmb3JjZXMgZ3Jvd3RoIHJhdGUgdG8gYmUgcG9zaXRpdmUuCgo0bTYKCmBzaWdtYSB+IGR1bmlmKDAsIHNxcnQoNjQpKWAuIGkuZS4gdmFyaWFuY2UgbXVzdCBiZSBiZXR3ZWVuIDAgYW5kIDY0LgoKIyMjIEhhcmQKCjRoMQoKYGBge3IgNGgxfQp3ZWlnaHQgPC0gYyg0Ni45NSwgNDMuNzIsIDY0Ljc4LCAzMi41OSwgNTQuNjMpCnN0ZF93ZWlnaHQgPC0gKHdlaWdodCAtIG1lYW4oZCR3ZWlnaHQpKSAvIHNkKGQkd2VpZ2h0KQpzaW1fd2VpZ2h0IDwtIHNpbShtNC41LCBkYXRhID0gbGlzdChzdGRfd2VpZ2h0ID0gc3RkX3dlaWdodCwgc3RkX3dlaWdodDIgPSBzdGRfd2VpZ2h0XjIpKQoKbWVhbl9oZWlnaHQgPC0gYXBwbHkoc2ltX3dlaWdodCwgMiwgbWVhbikKcGlfaGVpZ2h0IDwtIGFwcGx5KHNpbV93ZWlnaHQsIDIsIFBJKQpwaV9oZWlnaHRfZm10IDwtIHNwcmludGYoIiUwLjFmIC0gJTAuMWYiLCBwaV9oZWlnaHRbMSwgXSwgcGlfaGVpZ2h0WzIsIF0pCnRpYmJsZSgKICBJbmRpdmlkdWFsID0gMTo1LCAKICB3ZWlnaHQgPSB3ZWlnaHQsIAogIGBleHBlY3RlZCBoZWlnaHRgID0gbWVhbl9oZWlnaHQsCiAgYDg5JSBpbnRlcnZhbGAgPSBwaV9oZWlnaHRfZm10CikKYGBgCgpgYGB7ciA0aDJhfQpraWRzIDwtIGZpbHRlcihIb3dlbGwxLCBhZ2UgPCAxOCkgJT4lIAogIG11dGF0ZShzdGRfd2VpZ2h0ID0gKHdlaWdodCAtIG1lYW4od2VpZ2h0KSkgLyBzZCh3ZWlnaHQpKQptNGgyIDwtIHJldGhpbmtpbmc6Om1hcCgKICBhbGlzdCgKICAgIGhlaWdodCB+IGRub3JtKG11LCBzaWdtYSksCiAgICBtdSA8LSBhICsgYiAqIHN0ZF93ZWlnaHQsCiAgICBhIH4gZG5vcm0obWVhbihoZWlnaHQpLCAxMCksCiAgICBiIH4gZG5vcm0oMCwgMTApLAogICAgc2lnbWEgfiBkdW5pZigwLCAyNSkKICApLAogIGRhdGEgPSBraWRzCikKCnBvc3QgPC0gZXh0cmFjdC5zYW1wbGVzKG00aDIsIG4gPSAxZTQpCmRlbnMocG9zdCRhKQpkZW5zKHBvc3QkYikKZGVucyhwb3N0JHNpZ21hKQoKcHJlZF90YmwgPC0gdGliYmxlKAogIHN0ZF93ZWlnaHQgPSBzZXEobWluKGtpZHMkc3RkX3dlaWdodCksIAogICAgICAgICAgICAgICAgICAgbWF4KGtpZHMkc3RkX3dlaWdodCksIAogICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDEwMCksCiAgd2VpZ2h0ID0gc3RkX3dlaWdodCAqIHNkKGtpZHMkd2VpZ2h0KSArIG1lYW4oa2lkcyR3ZWlnaHQpLAogIG11ID0gY29lZihtNGgyKVsiYSJdICsgY29lZihtNGgyKVsiYiJdICogc3RkX3dlaWdodAopCgpnZ3Bsb3Qoa2lkcywgYWVzKHdlaWdodCwgaGVpZ2h0KSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSkgKwogIGdlb21fbGluZShhZXMoeSA9IG11KSwKICAgICAgICAgICAgcHJlZF90YmwpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCgptZWFuKHBvc3QkYikgLyBzZChraWRzJHdlaWdodCkKUEkocG9zdCRiKSAvIHNkKGtpZHMkd2VpZ2h0KQpgYGAKCk9uIGF2ZXJhZ2UsIGFuIGluY3JlYXNlIG9mIDEwIHVuaXRzIGluIHdlaWdodCBjb3JyZWxhdGVzIHdpdGggYW4gaW5jcmVhc2Ugb2YgMjcuMSAoMjYuMCAtIDI4LjIpIHVuaXRzIG8gaGVpZ2h0LgoKYGBge3IgNGgyYn0KIyBzdW1tYXJpemUgbW9kZWwKc2VxX3dlaWdodCA8LSBzZXEobWluKGtpZHMkc3RkX3dlaWdodCksIAogICAgICAgICAgICAgICAgICBtYXgoa2lkcyRzdGRfd2VpZ2h0KSwgCiAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSA1MCkKcHJlZF9kYXQgPC0gbGlzdChzdGRfd2VpZ2h0ID0gc2VxX3dlaWdodCkKbXUgPC0gbGluayhtNGgyLCBkYXRhID0gcHJlZF9kYXQpCm11Lm1lYW4gPC0gYXBwbHkobXUsIDIsIG1lYW4pCm11LlBJIDwtIGFwcGx5KG11LCAyLCBQSSwgcHJvYiA9IDAuODkpCnNpbS5oZWlnaHQgPC0gc2ltKG00aDIsIGRhdGEgPSBwcmVkX2RhdCkKaGVpZ2h0LlBJIDwtIGFwcGx5KHNpbS5oZWlnaHQsIDIsIFBJLCBwcm9iID0gMC44OSkKCiMgcGxvdCBmaXQKcHJlZF90YmwgPC0gdGliYmxlKAogIHN0ZF93ZWlnaHQgPSBzZXFfd2VpZ2h0LCAKICBtdV9tZWFuID0gbXUubWVhbiwKICBtdV9sb3cgPSBtdS5QSVsxLCBdLAogIG11X2hpZ2ggPSBtdS5QSVsyLCBdLAogIGhlaWdodF9sb3cgPSBoZWlnaHQuUElbMSwgXSwKICBoZWlnaHRfaGlnaCA9IGhlaWdodC5QSVsyLCBdCikgJT4lIAogIG11dGF0ZSh3ZWlnaHQgPSBzdGRfd2VpZ2h0ICogc2Qoa2lkcyR3ZWlnaHQpICsgbWVhbihraWRzJHdlaWdodCkpCgpnZ3Bsb3Qoa2lkcywgYWVzKHdlaWdodCwgaGVpZ2h0KSkgKyAKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGFscGhhID0gMC41KSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbXVfbWVhbiksIHByZWRfdGJsKSArCiAgZ2VvbV9yaWJib24oYWVzKHdlaWdodCwgeW1pbiA9IG11X2xvdywgeW1heCA9IG11X2hpZ2gpLAogICAgICAgICAgICAgIHByZWRfdGJsLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjYpICsKICBnZW9tX3JpYmJvbihhZXMod2VpZ2h0LCB5bWluID0gaGVpZ2h0X2xvdywgeW1heCA9IGhlaWdodF9oaWdoKSwKICAgICAgICAgICAgICBwcmVkX3RibCwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgIGFscGhhID0gMC4zKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAKClRoZSBtb2RlbCBvdmVyLXByZWRpY3RzIGhlaWdodCBmb3IgbG93L2hpZ2ggd2VpZ2h0cyBhbmQgdW5kZXItcHJlZGljdHMgaGVpZ2h0IGZvciB3ZWlnaHRzIG5lYXIgdGhlIG1lYW4uIEEgcG9seW5vbWlhbCBvciBhc3ltcHRvdGljIG1vZGVsIHdvdWxkIGNhcHR1cmUgdGhlIGN1cnZhdHVyZSBiZXR0ZXIuCgo0aDMKCmBgYHtyIDRoM30KbTRoMyA8LSByZXRoaW5raW5nOjptYXAoCiAgYWxpc3QoCiAgICBoZWlnaHQgfiBkbm9ybShtdSwgc2lnbWEpLAogICAgbXUgPC0gYSArIGIgKiBsb2cod2VpZ2h0KSwKICAgIGEgfiBkbm9ybSgxNzgsIDEwMCksCiAgICBiIH4gZG5vcm0oMCwgMTAwKSwKICAgIHNpZ21hIH4gZHVuaWYoMCwgNTApCiAgKSwKICBkYXRhID0gSG93ZWxsMQopCgojIHN1bW1hcml6ZSBtb2RlbApzZXFfd2VpZ2h0IDwtIHNlcShtaW4oSG93ZWxsMSR3ZWlnaHQpLCAKICAgICAgICAgICAgICAgICAgbWF4KEhvd2VsbDEkd2VpZ2h0KSwgCiAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSA1MCkKcHJlZF9kYXQgPC0gbGlzdCh3ZWlnaHQgPSBzZXFfd2VpZ2h0KQptdSA8LSBsaW5rKG00aDMsIGRhdGEgPSBwcmVkX2RhdCkKbXUubWVhbiA8LSBhcHBseShtdSwgMiwgbWVhbikKbXUuUEkgPC0gYXBwbHkobXUsIDIsIFBJLCBwcm9iID0gMC45NykKc2ltLmhlaWdodCA8LSBzaW0obTRoMywgZGF0YSA9IHByZWRfZGF0KQpoZWlnaHQuUEkgPC0gYXBwbHkoc2ltLmhlaWdodCwgMiwgUEksIHByb2IgPSAwLjk3KQoKIyBwbG90IGZpdApwcmVkX3RibCA8LSB0aWJibGUoCiAgd2VpZ2h0ID0gc2VxX3dlaWdodCwgCiAgbXVfbWVhbiA9IG11Lm1lYW4sCiAgbXVfbG93ID0gbXUuUElbMSwgXSwKICBtdV9oaWdoID0gbXUuUElbMiwgXSwKICBoZWlnaHRfbG93ID0gaGVpZ2h0LlBJWzEsIF0sCiAgaGVpZ2h0X2hpZ2ggPSBoZWlnaHQuUElbMiwgXQopCgpnZ3Bsb3QoSG93ZWxsMSwgYWVzKHdlaWdodCwgaGVpZ2h0KSkgKyAKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGFscGhhID0gMC41KSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbXVfbWVhbiksIHByZWRfdGJsKSArCiAgZ2VvbV9yaWJib24oYWVzKHdlaWdodCwgeW1pbiA9IG11X2xvdywgeW1heCA9IG11X2hpZ2gpLAogICAgICAgICAgICAgIHByZWRfdGJsLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjYpICsKICBnZW9tX3JpYmJvbihhZXMod2VpZ2h0LCB5bWluID0gaGVpZ2h0X2xvdywgeW1heCA9IGhlaWdodF9oaWdoKSwKICAgICAgICAgICAgICBwcmVkX3RibCwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgIGFscGhhID0gMC4zKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAK